package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.course.CategoryClient;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.course.CategoryBasicDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.vo.CouponDetailVO;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponScopeVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.enums.UserCouponStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.query.CouponQuery;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.C;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author lcm
 * @since 2023-08-01
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    private final ICouponScopeService scopeService;
    private final IExchangeCodeService codeService;
    private final CategoryClient categoryClient;
    private final IUserCouponService userCouponService;

    /**
     * 新增优惠券
     *
     * @param dto
     */

    @Override
    @Transactional
    public void saveCoupon(CouponFormDTO dto) {
        //1.属性拷贝
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        //2.保存优惠券
        save(coupon);
        //3.判断是否限定使用范围
        if(!dto.getSpecific()){
            return;
        }
        //4.保存限定范围的分类id至中间表Coupon_scope
        saveInCouponScope(dto,coupon);
    }

    private void saveInCouponScope(CouponFormDTO dto,Coupon coupon) {
        List<Long> list = dto.getScopes();
        if(CollUtils.isEmpty(list)){
            //如果使用范围有限定，却没有限定范围的分类id集合，传参出现了问题
            throw new BadRequestException("限定范围不为空！");
        }
        //4.1转换成Scope的Po对象集合
        List<CouponScope> scopes = list.stream()
                .map(bizId -> new CouponScope().setCouponId(coupon.getId()).setBizId(bizId))
                .collect(Collectors.toList());
        //5.将scopes批量新增至scope表
        scopeService.saveBatch(scopes);
    }

    /**
     * 分页查询优惠券
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<CouponPageVO> pageShowCoupon(CouponQuery query) {
        //1.获取分页查询的限定条件
        boolean isName=query.getName() !=null;
        boolean isType = query.getType() != null;
        boolean isStatus = query.getStatus() != null;
        //2.查询
        Page<Coupon> page = lambdaQuery()
                .eq(isName, Coupon::getName, query.getName())
                .eq(isType, Coupon::getType, query.getType())
                .eq(isStatus, Coupon::getStatus, query.getStatus())
                .page(query.toMpPageDefaultSortByCreateTimeDesc());
        //2.1健壮性处理
        List<Coupon> records = page.getRecords();
        if(CollUtils.isEmpty(records)){
            return PageDTO.empty(page);
        }
        //3.封装VO
        List<CouponPageVO> vos = BeanUtils.copyList(records, CouponPageVO.class);
        //4.返回结果
        return PageDTO.of(page,vos);
    }

    /**
     * 发放优惠券
     *
     * @param dto
     */
    @Override
    public void beginIssue(CouponIssueFormDTO dto) {
        //1.拿到优惠券对象
        Coupon coupon = getById(dto.getId());
        if(coupon == null){
            throw new BadRequestException("优惠券不存在！");
        }
        //2.判断优惠券状态
        if(coupon.getStatus() != CouponStatus.DRAFT && coupon.getStatus() != CouponStatus.PAUSE){
            throw new BadRequestException("优惠券状态错误！");
        }

        //3.判断优惠券的发放形式
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        //3.1如果发放开始时间为空或者发放开始时间在现在时间之前，代表立刻发放
        BeanUtils.copyProperties(dto,coupon);
        if(issueBeginTime == null || !issueBeginTime.isAfter(LocalDateTime.now())){
            coupon.setIssueBeginTime(LocalDateTime.now());
            coupon.setStatus(CouponStatus.ISSUING);
        }else {
            coupon.setStatus(CouponStatus.UN_ISSUE);
        }

        //4.更新优惠券
        updateById(coupon);

        //5.判断是否需要生成兑换码
        if(coupon.getStatus() == CouponStatus.DRAFT && coupon.getObtainWay()== ObtainType.ISSUE){
            codeService.asyncGenerateCode(coupon);
        }

    }

    /**
     * 修改优惠券
     *
     * @param dto
     */
    @Override
    @Transactional
    public void updateCoupon(CouponFormDTO dto) {
        //1，根据id获取po
        Coupon coupon = getById(dto.getId());
        //1.1判断优惠券是否存在
        if(ObjectUtils.isNull(coupon)){
            throw new BadRequestException("优惠券不存在！");
        }
        //2.判断po的状态是否是待发放
        if(coupon.getStatus() != CouponStatus.DRAFT){
            throw new BadRequestException("优惠券不可修改");
        }
        //2.1先清除原po中的限定范围值
        if(!coupon.getSpecific()){
            return;
        }
        removeScopes(coupon);
        //3.拷贝dto至po
        coupon = BeanUtils.copyBean(dto, Coupon.class);
        //3.1更新coupon
        updateById(coupon);
        //3.2同时判断是否需要更新限定范围
        if(!coupon.getSpecific()){
            return;
        }
        saveInCouponScope(dto,coupon);
    }

    private void removeScopes(Coupon coupon) {
        List<CouponScope> list = scopeService.lambdaQuery()
                .eq(CouponScope::getCouponId, coupon.getId())
                .list();
        //2.2批量移除
        Set<Long> ids = list.stream().map(CouponScope::getId).collect(Collectors.toSet());
        removeByIds(ids);
    }

    /**
     * 删除优惠券
     *
     * @param id
     */
    @Override
    @Transactional
    public void deleteCoupon(Long id) {
        //1.获取需要删除的po
        Coupon coupon = getById(id);
        if(ObjectUtils.isNull(coupon)){
            throw new BadRequestException("此优惠券不存在！");
        }
        if(coupon.getStatus() != CouponStatus.DRAFT){
            throw new BadRequestException("此优惠券状态错误");
        }
        //2.删除优惠券
        removeById(id);
        //3.同时删除此优惠券在scope表中的数据
        if(!coupon.getSpecific()){
            return;
        }
        removeScopes(coupon);
    }

    /**
     * 根据id查询优惠券
     *
     * @param id
     * @return
     */
    @Override
    @Transactional
    public CouponDetailVO getCouponById(Long id) {
        //1.获取对应优惠券po
        Coupon coupon = getById(id);
        if(ObjectUtils.isNull(coupon)){
            throw new BadRequestException("优惠券不存在！");
        }
        //2.拷贝至VO
        CouponDetailVO vo = BeanUtils.copyBean(coupon, CouponDetailVO.class);
        //3.查询scope表中的
        List<CouponScope> list = scopeService.lambdaQuery()
                .eq(CouponScope::getCouponId, coupon.getId())
                .list();

        if(CollUtils.isNotEmpty(list)){
            //3.查询限定范围的分类id
            List<CouponScopeVO> voList = list.stream().map(couponScope -> new CouponScopeVO().setId(couponScope.getBizId()))
                    .collect(Collectors.toList());
            //查询分类名称
            List<CategoryBasicDTO> all = categoryClient.getAllOfOneLevel();
            Map<Long, String> map = all.stream().collect(Collectors.toMap(CategoryBasicDTO::getId, CategoryBasicDTO::getName));
            voList.forEach(c -> c.setName(map.get(c.getId())));
            vo.setScopes(voList);
        }

        return vo;
    }

    /**
     * 暂停发放
     *
     * @param id
     */
    @Override
    public void pauseCoupon(Long id) {
        //1.获取到对应优惠券对象
        Coupon coupon = getById(id);
        if(ObjectUtils.isNull(coupon)){
            throw new BadRequestException("优惠券不存在！");
        }
        //2.只有进行中的优惠券可以被暂停
        if(coupon.getStatus()!=CouponStatus.ISSUING){
            throw new BadRequestException("该优惠券不可被暂停！");
        }
        //3.修改优惠券状态
        coupon.setStatus(CouponStatus.PAUSE);
        //4.更新
        updateById(coupon);
    }

    /**
     * 查询正在进行中的优惠券
     *
     * @return
     */
    @Override
    public List<CouponVO> queryIssueCoupons() {
        //1.查询正在发放的优惠券的列表
        List<Coupon> list = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();

        if(CollUtils.isEmpty(list)){
            return CollUtils.emptyList();
        }
        //2.统计用户领取优惠券的信息
        //2.1 查询优惠券id集合
        List<Long> couponIds = list.stream().map(Coupon::getId).collect(Collectors.toList());
        //2.2 查询当前用户的用户优惠券表信息
        List<UserCoupon> userCoupons = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .in(UserCoupon::getCouponId, couponIds)
                .list();
        //2.3 将信息按优惠券id分组，并统计数量
        Map<Long, Long> issuedMap = userCoupons.stream()
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        Map<Long, Long> unusedMap = userCoupons.stream()
                .filter(userCoupon -> userCoupon.getStatus() == UserCouponStatus.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));

        //3.封装VO返回
        List<CouponVO> voList=new ArrayList<>(list.size());
        for (Coupon coupon : list) {
            //3.1拷贝信息至vo
            CouponVO vo = BeanUtils.copyBean(coupon, CouponVO.class);
            //3.2封装领取信息 : 发放数量小于总数量并且当前用户领取数量小于每人限领数量
            vo.setAvailable(
                    coupon.getIssueNum() < coupon.getTotalNum() && issuedMap.getOrDefault(coupon.getId(),0L) < coupon.getUserLimit()
            );
            //3.3 使用信息 : 用户已领数量并且未使用的数量大于0
            vo.setReceived(
                    unusedMap.getOrDefault(coupon.getId(),0L) > 0
            );
            voList.add(vo);
        }
        return voList;
    }

    /**
     * 定时发放优惠券
     */
    @Override
    public void updateUnissuedCouponStatus(boolean flag) {
        //1.获取当前时间
        LocalDateTime now = LocalDateTime.now();
        List<Coupon> list1;
        if(flag){
            List<Coupon> list = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.UN_ISSUE)
                .le(Coupon::getIssueBeginTime,now)
                .list();
             list1 = list.stream().map(coupon -> coupon.setStatus(CouponStatus.ISSUING)).collect(Collectors.toList());
        }
        else{
            List<Coupon> list = lambdaQuery()
                    .eq(Coupon::getStatus, CouponStatus.UN_ISSUE)
                    .ge(Coupon::getIssueEndTime,now)
                    .list();
            list1 = list.stream().map(coupon -> coupon.setStatus(CouponStatus.FINISHED)).collect(Collectors.toList());
        }
        //2.更新状态
        updateBatchById(list1);

    }

}
